// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview This is the javascript that drives ToDone.
 * @author scotty@google.com (Scotty Allen)
 */


function ListMenu() {
  this.menu_element = $('#menu');

  var list_menu = this;
  var list_elements = this.menu_element.find('div.item').not('#newlist');
  list_elements.each(function() {
    list_menu.attachListListeners(this);
  });
}

// TODO(scotty): Maybe this should just take a TaskList instead.
ListMenu.prototype.addList = function(name, color, id) {
  var item = $("<div><span class=menuitemarrow>&#9658;</span>" +
               "<span class=name>" + name + "&nbsp;</span></div>");
  item.attr('id', 'item' + id);
  item.addClass('item');
  item.css('background', color);

  $(item).insertBefore(this.menu_element.find('#newlist'));
  this.attachListListeners(item.get(0));
}

ListMenu.prototype.removeList = function(id) {
  var element = this.menu_element.find('#item' + id);
  element.slideUp(500, function() {
    element.remove();
  });
}

ListMenu.prototype.updateListColor = function(id, color) {
  this.menu_element.find('#item' + id).css('background', color);
}

ListMenu.prototype.attachListListeners = function(element) {
  var id = 0;
  var match = element.id.match(/item(\d+)/);

  if (match) {
    id = match[1];
  }

  element.onclick = function () {
    var task_list_element = $('#tasklist' + id).get(0);
    var is_shown;
    if (task_list_element.style.display == 'none') {
      is_shown = 1;
      element.className = "item";
      $(task_list_element).animate({width: 'show', opacity: 'show'}, 500);
    } else {
      is_shown = 0;
      element.className = "item itemhidden";
      $(task_list_element).animate({width: 'hide', opacity: 'hide'}, 500);
    }
    $.ajax({type: "POST",
            url: "/task_list/update",
            // TODO(scotty): change this to use json
            dataType: "text",
            data: {id: id, is_shown: is_shown}
           });
    return false;
  }
}

function NewListCreator(list_menu) {
  this.form_element = $('#new_list_form');
  this.name_field = $('#new_list_field');
  this.link = $('#new_list_link');
  this.list_menu = list_menu;
};

NewListCreator.prototype.activate = function() {
  this.link.hide();
  this.form_element.fadeIn(500);
  this.name_field.get(0).focus();
  this.selected_color = '#fff';
};

NewListCreator.prototype.createList = function() {
  var name = this.name_field.val();
  var color = this.selected_color;
  var creator = this;
  $.ajax({type: "POST",
          url: "/task_list/new",
          // TODO(scotty): change this to use json
          dataType: "text",
          data: {name: name, color: color},
          success: function(id) { 
                     new TaskList(id, name, color);
                     creator.list_menu.addList(name, color, id);
                     creator.reset();
                   }
         });
  return false;
};

NewListCreator.prototype.reset = function() {
  this.name_field.get(0).blur();
  this.name_field.val('');
  this.selectColor(this.getSwatches()[0], '#fff');
  this.link.show();
  this.form_element.hide();
};

NewListCreator.prototype.getSwatches = function() {
  var children = $('#new_list_form').get(0).childNodes;
  var swatches = [];
  for (var i = 0; i < children.length; i++) {
    if (children[i].className &&
        children[i].className.match('swatch')) {
      swatches.push(children[i]);
    }
  }
  return swatches;
};

NewListCreator.prototype.selectColor = function (swatch_element,
                                                 swatch_color) {
  // Deselect previously selected swatch element
  var swatches = this.getSwatches();
  for (var i = 0; i < swatches.length; i++) {
    if (swatches[i].className.match("swatch_selected")) {
      swatches[i].className = "swatch";
    }
  }

  this.selected_color = swatch_color;
  swatch_element.className = "swatch swatch_selected";
};


function TaskList(id, name, color, opt_is_completed_tasks_shown) {
  this.id = id;
  this.color = color;
  if (opt_is_completed_tasks_shown != undefined) {
    this.is_completed_tasks_shown = opt_is_completed_tasks_shown;
  } else {
    this.is_completed_tasks_shown = true;
  }

  this.list_element = $('#tasklist' + id).get(0);
  if (!this.list_element) {
    this.createDom(name, color);
  }

  this.container_element = this.list_element.getElementsByTagName('div')[0];
  this.tasks_element = this.container_element.getElementsByTagName('ul')[0];

  var new_task_form = $(this.list_element).find('.newtask').find('form').get(0);
  this.new_task_creator = new NewTaskCreator(this, new_task_form, id);

  this.attachTasks();

  this.listmenuarrow = $(this.container_element).find('.listmenuarrow').get(0);

  var listmenuarrow_id = 'listmenuarrow' + this.id;
  var task_list = this;
  $(this.listmenuarrow).click(function () {
    return TaskList.prototype.showMenu.apply(task_list);
  });
}

TaskList.prototype.attachTasks = function() {
  var tasks = this.getTaskElements();
  for (var i = 0; i < tasks.length; i++) {
    new Task(tasks[i], this);
  }
};

TaskList.prototype.showMenu = function() {
  if (!this.menu) {
    this.createMenu();
  }
  var left = this.listmenuarrow.offsetLeft + $(this.listmenuarrow).width() -
             this.menu.width();
  this.menu.css('left', left + 'px');

  var top = this.listmenuarrow.offsetTop + $(this.listmenuarrow).height();
  this.menu.css('top', top + 'px');

  // Get around some weird rendering issues when showing the menu right away.
  // If we show it right away, it "jumps" in FF 2.0.
  var menu = this.menu;
  window.setTimeout(function(){ menu.show() }, 10);
  return false;
}

TaskList.prototype.menu_items = [
  {label: 'Show completed',
   click: function(icon) {
     if (this.is_completed_tasks_shown) {
       this.is_completed_tasks_shown = false;
       $(this.tasks_element).find('.checked').parent().slideUp(500);
       $(icon).css('visibility', 'hidden');
     } else {
       this.is_completed_tasks_shown = true;
       $(this.tasks_element).find('.checked').parent().slideDown(500);
       $(icon).css('visibility', 'visible');
     }
     $.ajax({type: 'POST',
             url: '/task_list/update',
             data: {id: this.id,
                    is_completed_tasks_shown:
                        this.is_completed_tasks_shown ? 1 : 0}});
   },
   iconClass: 'menu_checkmark',
   iconText: 'X',
   init: function(icon) {
     if (this.is_completed_tasks_shown) {
       $(icon).css('visibility', 'visible');
     } else {
       $(icon).css('visibility', 'hidden');
     }
   }
  },
  {label: 'Change color >',
   mouseover: function(menu_item) {
     this.createColorMenu(menu_item);
   }
  },
  {label: 'Rename list',
   click: function() {
     this.showRenameForm();
   }},
  {label: 'Delete list',
   click: function() {
     if (window.confirm("Are you sure you want to delete your list titled '" +
                        $(this.container_element).find('.name').text() + "'?")) {
       this.destroy();
     }
   }}
];

TaskList.prototype.createColorMenu = function(menu_item) {
  var color_menu = $('<ul class=color_menu></ul>');
  var task_list = this;
  for (var i = 0; i < colors.length; i++) {
    var swatch = $('<div class="swatch"></div>');
    swatch.css('background', colors[i]);
    if (this.color == colors[i]) {
      swatch.addClass('swatch_selected');
    }

    swatch.data('color', colors[i]);
    swatch.click(function () {
      var color = $(this).data('color');
      task_list.color = color;
      $(task_list.container_element).find('.title').css('background-color',
                                                        color);
      list_menu.updateListColor(task_list.id, color);
      $.ajax({type: 'POST',
              url: '/task_list/update',
              data: {id: task_list.id,
                     color: color}});
    });

    color_menu.append(swatch);
  }

  var left = menu_item.parent().get(0).offsetLeft +
             menu_item.get(0).offsetLeft + menu_item.width() + 2;
  color_menu.css('left', left + 'px');

  var top = menu_item.parent().get(0).offsetTop + menu_item.get(0).offsetTop;
  color_menu.css('top', top + 'px');

  color_menu.height(menu_item.height());

  menu_item.parent().parent().append(color_menu);
  this.color_menu = color_menu;
  return color_menu;
}

TaskList.prototype.createMenu = function() {
  var menu = $('<ul></ul>');
  menu.addClass('task_menu');
  menu.hide();
  var task_list = this;
  $.each(this.menu_items, function() {
    var menu_item = $('<li class=menuitem>' +
                      '<div class="icon"></div>' +
                      this.label + '</li>');
    var icon = menu_item.find('.icon');
    icon.html(this.iconText);

    if (this.click) {
      var click = this.click;
      menu_item.click(function() {
        click.apply(task_list, [icon]);
      });
    }
    if (this.mouseover) {
      var mouseover = this.mouseover;
      var this_menu_item = menu_item;
      menu_item.mouseover(function() {
        mouseover.apply(task_list, [this_menu_item]);
      });
    } else {
      // TODO(scotty): this is a nasty hack to hide the color menu, but it'll
      // work for now.  This should be refactored to fit better in the menu
      // model.
      menu_item.mouseover(function() {
        $('.color_menu').remove();
      });
    }
    if (this.mouseout) {
      var mouseout = this.mouseout;
      var this_menu_item = menu_item;
      menu_item.mouseout(function() {
        mouseout.apply(task_list, [this_menu_item]);
      });
    }
    menu_item.mouseover(function() {
      $(this).addClass('hover');
    });
    menu_item.mouseout(function() {
      $(this).removeClass('hover');
    });
    if (this.init) {
      this.init.apply(task_list, [icon]);
    }
    menu.append(menu_item);
  });
  $(this.container_element).append(menu);
  this.menu = menu;
}

$('html').click(function () {
  $('.task_menu').hide();
  $('.color_menu').remove();
} );

TaskList.prototype.showRenameForm = function () {
  var form_element = $('<form></form>');
  form_element.css('margin', 0);
  var name_input = $('<input type=input class=name_input>');
  form_element.append(name_input);
  var name_text =  $(this.container_element).find('.name');
  name_text.hide();
  name_input.val(name_text.text());
  $(this.container_element).find('.title').append(form_element);
  name_input.each(function() {
    this.focus();
    this.select();
  });
  var task_list = this;

  name_input.blur(function () {
    task_list.hideRenameForm();
  });

  form_element.submit(function () {
    var name = name_input.val();
    name_text.text(name);
    $('#menu').find('#item' + task_list.id).find('.name').html(name);
    $.ajax({type: 'POST',
            url: '/task_list/update',
            data: {id: task_list.id,
                   name: name}
           });
    task_list.hideRenameForm();
    return false;
  });

};

TaskList.prototype.hideRenameForm = function () {
  var name_input = $(this.container_element).find('.name_input')
  name_input.parent().remove();
  var name_text =  $(this.container_element).find('.name');
  name_text.show();
}

TaskList.prototype.destroy = function () {
  $.ajax({type: 'POST',
          url: '/task_list/delete',
          data: {id: this.id}});
  var id = this.id;
  var list_element = $(this.list_element);
  $(this.list_element).animate({width: 'hide', opacity: 'hide'}, 500,
                               function() {
                                 list_element.remove();
                               });
  list_menu.removeList(id);
}

function Task(element, task_list) {
  this.element = element;
  this.task_list = task_list;
  var match = element.id.match(/task(\d+)/);
  if (match) {
    this.id = match[1];
  }
  this.attachCheckbox();
}

Task.prototype.attachCheckbox = function() {
  this.checkbox = this.element.getElementsByTagName('div')[0];
  var task = this;
  this.checkbox.onclick = function () {
    return Task.prototype.onClick.apply(task);
  };
};

Task.prototype.onClick = function() {
  this.setDone(this.checkbox.className == 'checkbox');
};

Task.prototype.setDone = function(done) {
  if (done) {
    this.checkbox.className = 'checked';
    if (!this.task_list.is_completed_tasks_shown) {
      $(this.element).css('background', '#fffa7a');
      $(this.element).fadeOut(1000, function() {
        $(this).css('background', '#fff');
      });
    }
  } else {
    this.checkbox.className = 'checkbox';
  }

  var params = {done: done ? 1 : 0,
                id: this.id
               };

  $.ajax({type: "POST",
          url: "/task/update",
          data: {done: done? 1 : 0,
                 id: this.id}
         });
};


TaskList.prototype.getTaskElements = function() {
  var tasks = [];
  var elements =this.tasks_element.getElementsByTagName('li')
  for (var i = 0; i < elements.length; i++) {
    if (elements[i].className == 'task') {
      tasks.push(elements[i]);
    }
  }
  return tasks;
};

TaskList.prototype.createDom = function(name, color) {
  this.list_element = document.createElement('li');
  this.list_element.className = 'tasklist';
  this.list_element.id = 'tasklist' + this.id;
  // TODO(scotty): Change this to use a compiled template from the django
  // template.
  var html = "<div class=tasklist>" +
              "<div class=title style='background: " + color + "'>" +
                "<div class=lcorner></div>" + 
                "<div class=rcorner></div>" +
                "<div class=name>" + name + "</div>" +
                "<div class=listmenuarrow>&#9660;</div>" +
              "</div>" +
              "<ul class=tasks>" +
              "</ul>" +
              "<div class=newtask>" +
                "<form class=new_task_form>" +
                  "<input name=newtaskinput class=newtaskinput " +
                         "value='new task'> " +
                  "<input class=addbutton type=submit value=''>" +
                "</form>" +
              "</div>" +
            "</div>";
  this.list_element.innerHTML = html;
  $('#tasklists').get(0).appendChild(this.list_element);
}

TaskList.prototype.addTask = function (title, id) {
  var task = document.createElement('li');
  task.id = 'task' + id;
  task.className = 'task';
  task.innerHTML = "<div class=checkbox></div>" + title;
  this.tasks_element.appendChild(task);
  new Task(task, this);
}

function NewTaskCreator(task_list, form_element, task_list_id) {
  this.task_list = task_list;
  this.form_element = form_element;
  this.box = $(form_element).find('.newtaskinput').get(0);
  this.task_list_id = task_list_id;
  var object = this;
  this.box.onfocus = function () {
    return NewTaskCreator.prototype.on_focus.apply(object);
  };
  this.box.onblur = function () {
    return NewTaskCreator.prototype.on_blur.apply(object);
  };

  this.form_element.onsubmit = function () {
    return NewTaskCreator.prototype.on_submit.apply(object);
  };
}

NewTaskCreator.prototype.on_focus = function() {
  if (this.box.value == 'new task') {
    this.box.value = '';
    this.box.className = 'newtaskinput_selected';
  }
};

NewTaskCreator.prototype.on_blur = function() {
  if (this.box.value == '') {
    this.box.className = 'newtaskinput';
    this.box.value = 'new task';
  }
};

NewTaskCreator.prototype.on_submit = function() {
  var title = this.box.value;
  if (title == '' || title == 'new task') {
    // Don't do anything if they didn't specify a task
    return false;
  }
  var params = {title: title,
                task_list_id: this.task_list_id};

  var creator = this;
  $.ajax({type: "POST",
          url: "/task/new",
          data: {title: title,
                 task_list_id: this.task_list_id},
          success: function(id) { 
            creator.task_list.addTask(title, id);
            creator.box.value = '';
          }
         });

  return false;
};

function joinParameters(params) {
  var paramParts = [];
  for (param in params) {
    paramParts.push(param + '=' + params[param]);
  }
  return paramParts.join('&');
}
